(use-modules
 (rnrs exceptions)
 (rnrs conditions))

;; =========
;; UTILITIES
;; =========

(define even-simpler-display
  (lambda (sth)
    (display
     (simple-format
      #f "~a\n" sth))))

;; =====
;; INTRO
;; =====

;; Conditions are a concept from R6RS exception handling. A condition is created
;; using the constructor ~condition~ from R6RS or

;; Conditions can be "simple" or "compound". A simple condition describes a
;; single aspect of an exceptional situation. A compound condition contains a
;; list of simple conditions, which together describe multiple aspects of an
;; exceptional situation. The list of simple conditions of a compound condition
;; is called components of the compound condition.

;; Next let us see how we can work with conditions, when we get them.

;; ==========
;; CONDITIONS
;; ==========

;; The standard already defines a few constructors for so called standard
;; condition types:

;; - (make-message-condition message)
;; - (make-warning)
;; - (make-serious-condition)  ; intended as supertype
;; - (make-violation)
;; - (make-assertion-violation)
;; - (make-irritants-condition irritants)
;; - (make-who-condition who)
;; - (make-non-continuable-violation)
;; - (make-implementation-restriction-violation)
;; - (make-lexical-violation)
;; - (make-syntax-violation form subform)
;; - (make-undefined-violation)

;; These have a predefined purpose, but probably already cover a lot of
;; ground. Lets try to make use of them:

(define divide
  (lambda (a b)
    (cond
     [(= b 0)
      (raise  ; raise from rnrs exceptions
       (condition
        (make-error)
        (make-message-condition "division by zero")
        (make-irritants-condition (list a b))
        (make-who-condition 'divide)))]
     [else
      (/ a b)])))


(with-exception-handler
    (lambda (conditions-or-value)
      (even-simpler-display
       ;; We can get the simple conditions from a compound condition with the
       ;; `simple-conditions` getter.
       (simple-conditions conditions-or-value)))
  (lambda ()
    (divide 2 0))
  ;;  We need to unwind the stack here. For explanation see the other examples.
  #:unwind? #t)

;; ======
;; GUARDS
;; ======

;; R6RS's or R7RS's guard expression is a wrapper around with-exception-handler
;; with #:unwind? set to #t. It is a macro enabling some convenience. The guard
;; expression allows for a cond expression, which enables to specify different
;; handlers for different exception.

(guard
    ;; First a binding is defined, which will be bound to the raised
    ;; object. This can be a condition or a raised value.
    (con
     ;; First we check, whether the condition is an error. This could be a
     ;; compound condition, which contains a simple condition produced by
     ;; `make-error`, or it could be only a simple condition produced by
     ;; `make-error`.
     [(error? con)
      (if (message-condition? con)
          (even-simpler-display (condition-message con))
          (even-simpler-display "an error has occurred"))
      ;; We also return the symbol 'error, to signal, that some kind of error
      ;; occurred to whatever expects a return value from the guard form.
      'error]
     ;; We can in other cases of the guard check for different errors.
     [(violation? con)
      (if (message-condition? con)
          (even-simpler-display (condition-message con))
          (even-simpler-display "the program has a bug"))
      'violation])
  ;; Write something here, which could cause an exceptional situation.
  (raise
   (condition
    (make-error)
    (make-message-condition "I am an error"))))

;; If no branch of the guard catches the condition, it will be raised again.

(guard
    (con
     ((error? con)
      (if (message-condition? con)
          (display (condition-message con))
          (display "an error has occurred"))
      'error))
  (raise
    (condition
      (make-violation
       (make-message-condition "I am an error")))))

;; Another example:
(guard
    (con
     [(error? con)
      (if (message-condition? con)
          (even-simpler-display (condition-message con))
          (even-simpler-display "an error has occurred"))
      'error]
     [else])
  (divide 314 0))

;; Advantages of using `guard`: We can see, that the guard makes it unnecessary
;; to wrap the code we want to run in a lambda to delay evaluation. This is
;; taken care of by the guard form itself. Also the guard form already provides
;; the cond like structure, so there is no need to write a cond.
